const BaseDao = require('./BaseDao');
const Trade = require('../model/Trade');
const { Op } = require('../config/DBconnector');
const { TradeStatus } = require('../constants/DbEnum');

/**
 * Trade DAO
 */
class TradeDao extends BaseDao {
  /**
   * constructor
   */
  constructor() {
    super(Trade);
  }

  /**
   * get first price from the date
   * @param {CoinType} baseCoinType
   * @param {CoinType} counterCoinType
   * @param {Date} from
   * @return {Decimal} price
   */
  getFirstPriceFrom(baseCoinType, counterCoinType, from) {
    return this.getFirstPriceBetween(baseCoinType, counterCoinType, from, null);
  }

  /**
   * getFirstPriceBetween
   * @param {CoinType} baseCoinType
   * @param {CoinType} counterCoinType
   * @param {Date} from
   * @param {Date} to
   * @return {Decimal} price
   */
  getFirstPriceBetween(baseCoinType, counterCoinType, from, to) {
    const attributes = ['price'];
    const where = this._buildWhereForCompleted(baseCoinType, counterCoinType, from, to);
    const order = [['updatedAt', 'asc']];

    return this.model.findOne({ attributes, where, order }).then((result) => {
      return result ? result.price : null;
    });
  }

  /**
   * get last price to the date
   * @param {CoinType} baseCoinType
   * @param {CoinType} counterCoinType
   * @param {Date} to
   * @return {Decimal} price
   */
  getLastPriceTo(baseCoinType, counterCoinType, to) {
    return this.getLastPriceBetween(baseCoinType, counterCoinType, null, to);
  }

  /**
   * get last price to the date
   * @param {CoinType} baseCoinType
   * @param {CoinType} counterCoinType
   * @param {Date} from
   * @param {Date} to
   * @return {Decimal} price
   */
  getLastPriceBetween(baseCoinType, counterCoinType, from, to) {
    const attributes = ['price'];
    const where = this._buildWhereForCompleted(baseCoinType, counterCoinType, from, to);
    const order = [['updatedAt', 'desc']];

    return this.model.findOne({ attributes, where, order }).then((result) => {
      return result ? result.price : null;
    });
  }

  /**
   * count trade volume in date range
   * @param {CoinType} baseCoinType
   * @param {CoinType} counterCoinType
   * @param {Date} from
   * @param {Date} to
   * @return {Decimal} baseCoinAmount
   */
  countTradeVolume(baseCoinType, counterCoinType, from, to) {
    const where = this._buildWhereForCompleted(baseCoinType, counterCoinType, from, to);

    return this.model.sum('baseCoinAmount', { where }).then((result) => {
      return result ? result : null;
    });
  }

  /**
   * count trade amount in date range
   * @param {CoinType} baseCoinType
   * @param {CoinType} counterCoinType
   * @param {Date} from
   * @param {Date} to
   * @return {Decimal} counterCoinAmount
   */
  countTradeAmount(baseCoinType, counterCoinType, from, to) {
    const where = this._buildWhereForCompleted(baseCoinType, counterCoinType, from, to);

    return this.model.sum('counterCoinAmount', { where }).then((result) => {
      return result ? result : null;
    });
  }

  /**
   * get max price in date range
   * @param {CoinType} baseCoinType
   * @param {CoinType} counterCoinType
   * @param {Date} from
   * @param {Date} to
   * @return {Decimal} price
   */
  getTopPrice(baseCoinType, counterCoinType, from, to) {
    const where = this._buildWhereForCompleted(baseCoinType, counterCoinType, from, to);

    return this.model.max('price', { where }).then((result) => {
      return result ? result : null;
    });
  }

  /**
   * get min price in date range
   * @param {CoinType} baseCoinType
   * @param {CoinType} counterCoinType
   * @param {Date} from
   * @param {Date} to
   * @return {Decimal} price
   */
  getBottomPrice(baseCoinType, counterCoinType, from, to) {
    const where = this._buildWhereForCompleted(baseCoinType, counterCoinType, from, to);

    return this.model.min('price', { where }).then((result) => {
      return result ? result : null;
    });
  }

  /**
   * build where object for completed trade
   * @param {CoinType} baseCoinType
   * @param {CoinType} counterCoinType
   * @param {Date} from
   * @param {Date} to
   * @return {Object<where>} where
   * @private
   */
  _buildWhereForCompleted(baseCoinType, counterCoinType, from, to) {
    let where = {
      baseCoinType,
      counterCoinType,
      status: TradeStatus.COMPLETED,
    };

    if (from && to) {
      Object.assign(where, { updatedAt: { [Op.between]: [from, to] } });
    } else if (from) {
      Object.assign(where, { updatedAt: { [Op.gte]: from } });
    } else if (to) {
      Object.assign(where, { updatedAt: { [Op.lte]: to } });
    }

    return where;
  }
}

module.exports = new TradeDao();
